<script setup>
import { ref, onMounted, toRefs, getCurrentInstance } from 'vue'
import * as THREE from 'three'
// import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import Loader from '../../../utils/3d/ziploader'

import tshirt from '../../../assets/img/t-shirt.jpg'
import hairs from '../../../assets/img/hair.png'

const { appContext } = getCurrentInstance()
const proxy = appContext.config.globalProperties

const props = defineProps({
  modelLogo: {
    type: String,
    default: ''
  }
})

const {
  modelLogo
} = toRefs(props)

const tShirt = modelLogo.value ? modelLogo.value : tshirt
// 视图
const viewer = ref()
// 场景
const Scene = new THREE.Scene()
// 相机
let Camera = null
// 渲染器
const Renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
// 动画控制器
let animationMixer = null
// 动画相关 - 记录模型上一个动画片段
let previousAction = null
// 动画相关 - 记录模型激活动画片段
let activeAction = null
// 当前模型动画组
let actions = null
// 动画片段名称
const emotes = []
// 射线检测组
const RaycasterArr = []
// 鼠标按下时间
let mouseDownTime = null

// let controls = null

// 模型宽高
const w = 280
const h = 756

// 初始化
const initBase = () => {
  Camera = new THREE.PerspectiveCamera(43, w / h, 0.01, 1000)
  Camera.position.set(0.0030739105726107774, 1.6840946140722628, 2.914221510775541)
  Camera.rotation.set(-0.19931655611806948, -0.01413232628315416, -0.002854607436976826)
  Scene.add(Camera)

  // controls = new OrbitControls(Camera, Renderer.domElement)

  const targetObject = new THREE.Object3D()
  targetObject.position.set(0, 1.4, 0.2) // 将目标点设置到你希望的位置
  Scene.add(targetObject)
  // 聚光灯（主灯，光影效果）
  const spotLight = new THREE.SpotLight('#ffffff', 0.4)
  spotLight.position.set(0, 3, 3.5)
  spotLight.angle = Math.PI / 4
  spotLight.castShadow = true
  Scene.add(spotLight)
  // const spotLightHelper = new THREE.SpotLightHelper(spotLight)
  // Scene.add(spotLightHelper)

  const directionalLight1 = new THREE.DirectionalLight('#ffffff', 0.2)
  directionalLight1.position.set(0, 1.4, 3.5)
  directionalLight1.target = targetObject
  Scene.add(directionalLight1)

  const spotLight1 = new THREE.DirectionalLight('#ffecec', 0.5)
  spotLight1.position.set(-2, 3, 0.5)
  spotLight1.angle = Math.PI / 4
  spotLight1.castShadow = true
  spotLight1.target = targetObject
  Scene.add(spotLight1)
  // const directionalLight = new THREE.DirectionalLightHelper(spotLight1)
  // Scene.add(directionalLight)

  const spotLight2 = new THREE.DirectionalLight('#3f4fff', 0.5)
  spotLight2.position.set(2, 3, 0.5)
  spotLight2.angle = Math.PI / 4
  spotLight2.castShadow = true
  Scene.add(spotLight2)
  // const spotLightHelper2 = new THREE.DirectionalLightHelper(spotLight2)
  // Scene.add(spotLightHelper2)

  // 环境光（辅灯，暗部提亮）
  const ambientLight = new THREE.AmbientLight('#ffffff', 0.3)
  Scene.add(ambientLight)

  // 渲染器
  Renderer.outputEncoding = THREE.sRGBEncoding
  Renderer.shadowMap.enabled = true
  Renderer.shadowMap.type = THREE.PCFSoftShadowMap

  Renderer.setPixelRatio(window.devicePixelRatio)
  Renderer.setSize(w, h)
  viewer.value.appendChild(Renderer.domElement)
}

// 初始化Loader类型和对应的3d模型地址
const url = `model/SZR_N.zip?v=${import.meta.env.VITE_MODEL_VERSION}`

const initModel = () => {
  Loader.ZipLoader(url, onProgress, proxy)
    .then(([model]) => {
      setAnimation(model)

      const _model = model.scene
      _model.position.set(0, 0, 0)
      _model.rotation.y = THREE.MathUtils.degToRad(0)

      _model.traverse((child) => {
        if (child.isMesh) {
          child.frustumCulled = false
        }

        if (child.name === 'BKC_ARTshirt_5') {
          const loader = new THREE.TextureLoader()
          loader.load(
            tShirt,
            function (texture) {
              texture.flipY = false
              child.material.map = texture
              child.material.needsUpdate = true
            }
          )
          // 上衣 - 阴影
          child.receiveShadow = true
        }

        if (child.name === 'polySurface183') {
          const loader = new THREE.TextureLoader()
          loader.load(hairs, function (texture) {
            child.material.alphaMap = texture
            child.material.opacity = 1
            child.material.transparent = true
          })
        }
      })

      const geometry = new THREE.PlaneGeometry(1, 1)
      const material = new THREE.MeshBasicMaterial({ color: 'green', side: THREE.DoubleSide, transparent: true, opacity: 0 })
      const plane = new THREE.Mesh(geometry, material)
      Scene.add(plane)
      plane.scale.set(0.4, 1.9, 0.5)
      plane.position.set(0.1, 1.07, 0.1)
      RaycasterArr.push(plane)

      Scene.add(_model)
      // 加载完毕
      Emit('isLoaded')
    })
    .catch((error) => console.log(error))
}
// 设置模型执行的动画
const setAnimation = (model) => {
  const animaName = [
    'speak',
    'welcome',
    'standby'
  ]
  animationMixer = new THREE.AnimationMixer(model.scene)
  const modelAction = model.animations[0]
  actions = {}

  // 模型裁剪
  if (emotes.length === 0) {
    emotes.push(animationMixer.clipAction(THREE.AnimationUtils.subclip(modelAction, 'speak', 0, 340, 60)))
    emotes.push(animationMixer.clipAction(THREE.AnimationUtils.subclip(modelAction, 'welcome', 341, 601, 60)))
    emotes.push(animationMixer.clipAction(THREE.AnimationUtils.subclip(modelAction, 'standby', 609, 952, 60)))
  }
  for (let i = 0; i < emotes.length; i++) {
    actions[emotes[i]._clip.name] = emotes[i]
    const action = emotes[i]
    if (animaName.indexOf(emotes[i]._clip.name) >= 0) {
      if (emotes[i]._clip.name === 'speak' || emotes[i]._clip.name === 'standby') {
        action.clampWhenFinished = false
        action.loop = THREE.LoopRepeat
      } else {
        action.clampWhenFinished = true
        action.loop = THREE.LoopOnce
      }
    }
  }
  activeAction = actions.standby
  activeAction.play()
}

// 动画渐入渐出（当前动画到选择动画）
const animationCallback = (name) => {
  fadeToAction(name, 0.2)
  animationMixer.addEventListener('finished', restoreState)
}
// 动画渐入渐出（当前动画返回默认动画）
const restoreState = () => {
  animationMixer.removeEventListener('finished', restoreState)
  fadeToAction('standby', 0.2)
}
// 动画渐入渐出 name动画片段名称 duration动画渐入渐出时间
const fadeToAction = (name, duration) => {
  previousAction = activeAction
  activeAction = actions[name]
  if (previousAction !== activeAction) {
    previousAction.fadeOut(duration)
  }
  activeAction?.reset()?.setEffectiveTimeScale(1)?.setEffectiveWeight(1)?.fadeIn(duration)?.play()
}

// 初始化消息监听
const initEvents = () => {
  // 射线检测(鼠标指向物体交互)
  const getIntersects = (mouseEvent) => {
    // 将鼠标位置归一化为设备坐标，x和y方向的取值范围是（-1 to +1）
    const m = Renderer.domElement.getBoundingClientRect()
    const x = ((mouseEvent.clientX - m.left) / m.width) * 2 - 1
    const y = -((mouseEvent.clientY - m.top) / m.height) * 2 + 1
    const raycaster = new THREE.Raycaster()
    // 通过鼠标点的位置和当前相机的矩阵计算出raycaster
    raycaster.setFromCamera(new THREE.Vector2(x, y), Camera)
    // 根据当前选中的区域层级进行对应模型的射线检测
    return raycaster.intersectObjects(RaycasterArr, false)
  }
  // 鼠标按下
  Renderer.domElement.addEventListener('mousedown', (event) => {
    mouseDownTime = new Date().getTime()
  })
  Renderer.domElement.addEventListener('mouseup', (event) => {
    if (new Date().getTime() - mouseDownTime > 200) return
    const intersects = getIntersects(event)
    if (intersects.length > 0) {
      // 点击身体
      Emit('clickBody')
    }
  })
}

// 实时渲染
const initRender = () => {
  let time = 0
  const animationClock = new THREE.Clock()
  const fpsClock = new THREE.Clock()
  const animate = () => {
    time += fpsClock.getDelta()
    // 帧率控制（30）
    if (time > 1 / 30) {
      // 渲染
      Renderer.render(Scene, Camera)
      // if (controls) {
      //   controls.update()
      //   console.log('position')
      //   console.log(Camera.position)
      //   console.log('rotation')
      //   console.log(Camera.rotation)
      // }
      // 动画
      if (animationMixer) {
        const clockDelta = animationClock.getDelta()
        animationMixer.update(clockDelta)
      }
      time = 0
    }
    requestAnimationFrame(animate)
  }
  animate()
}

// 响应事件（向父组件通信）
const Emit = defineEmits(['isLoaded', 'onProgress', 'clickBody'])

const onProgress = function (xhr) {
  const p = Math.floor(xhr.loaded / xhr.total * 100)
  Emit('onProgress', p)
}
// 输出方法（供父组件调用）
defineExpose({ animationCallback })

onMounted(() => {
  initBase()
  initModel()
  initRender()
  initEvents()
})

</script>

<template>
  <div
    class="digital-people"
    ref="viewer"
    @click="clickModel"
  />
</template>

<style lang="less">
.digital-people{
    width: 100%;
    height: 100%;
}
</style>
